package net.ankao.org.easylock.utils;

import net.ankao.org.easylock.aop.EasyLockAspect;
import net.ankao.org.easylock.exception.EasyLockException;
import net.ankao.org.easylock.core.EasyLockLambdaCallback;
import net.ankao.org.easylock.core.EasyLockTemplate;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 分布式锁工具类
 * </p>
 *
 * @author lilu
 * @since 2022/3/29
 */
public class EasyLockUtil {



    private RedissonClient redisson;

    private EasyLockUtil() {
    }


    public EasyLockUtil(RedissonClient redisson) {
        this.redisson = redisson;
    }

    /**
     *
     * @param lockName 锁名称
     * @param callback 需要分布式锁 锁住的代码块
     * @param <T> 返回泛型
     * @return 执行结果
     * @throws Throwable 异常
     */
    public <T> T tryLock(String lockName,  EasyLockLambdaCallback<T> callback) throws Throwable {
        return this.tryLock(lockName, EasyLockTemplate.DEFAULT_WAIT_TIME, EasyLockTemplate.DEFAULT_TIMEOUT, EasyLockTemplate.DEFAULT_TIME_UNIT,callback);
    }


    public <T> T tryLock(String lockName, long waitTime, long leaseTime,  TimeUnit timeUnit,EasyLockLambdaCallback<T> callback) throws Throwable {
       return this.tryLock(lockName, waitTime, leaseTime, timeUnit,false,callback);
    }


    public <T> T tryLock(String lockName, long waitTime, long leaseTime,  TimeUnit timeUnit, boolean fairLock,EasyLockLambdaCallback<T> callback) throws Throwable {
        RLock lock = getLock(redisson, EasyLockAspect.LOCK_PREFIX + lockName, fairLock);
        try {
            if (lock.tryLock(waitTime, leaseTime, timeUnit)) {
                return callback.process();
            } else {
                throw new EasyLockException("分布式锁被占用，获取超时！");
            }
        } catch (InterruptedException e) {
            throw new EasyLockException(e);
        } finally {
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }


    private RLock getLock(RedissonClient redisson, String lockName, boolean fairLock) {
        RLock lock;
        if (fairLock) {
            lock = redisson.getFairLock(lockName);
        } else {
            lock = redisson.getLock(lockName);
        }
        return lock;
    }
}
